import type { ReactElement, ReactNode } from 'react';
import * as React from 'react';
import extractGradient from 'react-native-svg/src/lib/extract/extractGradient';
import type {
  NumberProp,
  TransformProps,
  Units,
} from 'react-native-svg/src/lib/extract/types';
import Shape from 'react-native-svg/src/elements/Shape';
import RNSVGLinearGradient from 'react-native-svg/src/fabric/LinearGradientNativeComponent';
import { stringifyPropsForFabric } from 'react-native-svg/src/lib/extract/extractProps';
import type { NativeMethods } from 'react-native';
import Stop from 'react-native-svg/src/elements/Stop';

export interface LinearGradientProps {
  children?: ReactElement[];
  x1?: NumberProp;
  x2?: NumberProp;
  y1?: NumberProp;
  y2?: NumberProp;
  gradientUnits?: Units;
  gradientTransform?: TransformProps['transform'];
  id?: string;
}

const isStopComponent = (
  child: ReactNode,
): child is ReactElement<Stop['props']> => {
  // @ts-ignore
  return React.isValidElement(child) && child.type!.displayName === 'Stop';
};

export default class LinearGradient extends Shape<LinearGradientProps> {
  static displayName = 'LinearGradient';

  static defaultProps = {
    x1: '0%',
    y1: '0%',
    x2: '100%',
    y2: '0%',
  };

  private get gradientKey(): string {
    const children = React.Children.toArray(this.props.children);
    const stops = children.filter(isStopComponent);
    return stops
      .map(
        stop =>
          `${stop.props.offset}-${String(stop.props.stopColor)}-${stop.props.stopOpacity}`,
      )
      .join('|');
  }

  render() {
    const { props } = this;
    const { x1, y1, x2, y2 } = props;
    const linearGradientProps = stringifyPropsForFabric({ x1, y1, x2, y2 });
    return (
      <RNSVGLinearGradient
        key={this.gradientKey}
        ref={(ref) =>
          this.refMethod(ref as (LinearGradient & NativeMethods) | null)
        }
        {...linearGradientProps}
        {...extractGradient(props, this)}
      />
    );
  }
}